

#include <stddef.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include <new>
#include <sstream>
#include <map>
#include <set>
#include <iostream>

#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <pwd.h>
#include <linux/param.h>

#include "core.h"
#include "proc.h"
#include "utils.h"
#include "system.h"

using namespace std;

string ProcFsUtils::getProcFilePath(pid_t pid, const char *file)
{
    ostringstream ss;
    ss << "/proc/" << pid << "/" << file;
    return ss.str();
}

string ProcFsUtils::getProcFileContent(pid_t pid, const char *file)
{
    return getFileContent(getProcFilePath(pid, file).c_str());
}

/*
 * 功能：保证DIR被释放
 */
class DIRHolder
{
private:
    DIR *pdir;
public:
    DIRHolder():pdir(NULL) {}

    ~DIRHolder() {
        if (pdir != NULL) {
            closedir(pdir);
        }
    }

    DIR * open(const char *path) {
        pdir = opendir(path);
        return pdir;
    }
};

/*
 * 把一个C字符串转换为PID，如果转换失败则返回-1
 */
static pid_t to_pid(const char *s)
{
    pid_t res = 0;

    for (const char *p = s; *p != '\0'; ++p) {
        char ch = *p;
        if (!isdigit(ch)) {
            return res;
        } else {
            res = 10 * res + (ch - '0');
        }
    }
    return res;
}

static inline pid_t to_pid(const string &s)
{
    return to_pid(s.c_str());
}

static inline bool is_pid(const char *s)
{
    for (const char *p = s; *p != '\0'; ++p) {
        if (!isdigit(*p)) {
            return false;
        }
    }
    return true;
}

static inline bool is_pid(const string &s)
{
    return is_pid(s.c_str());
}

class BufHolder
{
public:

    char *buf;
    
    BufHolder() {
        size_t len = offsetof(dirent, d_name) + NAME_MAX + 1;
	buf = new char[len];
    }

    ~BufHolder() {
        if (buf) {
            delete [] buf;
        }
    }
};

void ProcFsUtils::getAllPid(set<pid_t> *all_pid)
{
    all_pid->clear();

    DIRHolder dirHolder;
    DIR *dir = dirHolder.open("/proc");
    if (dir == NULL) {
        ERROR(strerror(errno));
    }

    BufHolder buf_holder;
    dirent *entryp = (dirent *)(buf_holder.buf);
    new(entryp)dirent;

    dirent *res = NULL;

    int n;
    while ((n = readdir_r(dir, entryp, &res)) == 0) {
        if (is_pid(entryp->d_name)) {
            all_pid->insert(to_pid(entryp->d_name));
        }
        if (res == NULL) {
            break;
        }
    }

    if (n != 0) {
        ERROR(strerror(n));
    }

}

void TaskStatusFileContent::loadFromText(const string &text)
{
    Properties p;
    p.loadFromText(text);

#ifdef P_LOAD
#error defined
#else
#define P_LOAD(P)\
    P = p.contains(#P) ? p.value(#P) : "";

    P_LOAD(Name)
    P_LOAD(State)
    P_LOAD(Tgid)
    P_LOAD(Ngid)
    P_LOAD(Pid)
    P_LOAD(PPid)
    P_LOAD(TracerPid)
    P_LOAD(Uid)
    P_LOAD(Gid)
    P_LOAD(FDSize)
    P_LOAD(Groups)
    P_LOAD(VmPeak)
    P_LOAD(VmSize)
    P_LOAD(VmLck)
    P_LOAD(VmPin)
    P_LOAD(VmHWM)
    P_LOAD(VmRSS)
    P_LOAD(VmData)
    P_LOAD(VmStk)
    P_LOAD(VmExe)
    P_LOAD(VmLib)
    P_LOAD(VmPTE)
    P_LOAD(VmSwap)
    P_LOAD(Threads)
    P_LOAD(SigQ)
    P_LOAD(SigPnd)
    P_LOAD(ShdPnd)
    P_LOAD(SigBlk)
    P_LOAD(SigIgn)
    P_LOAD(SigCgt)
    P_LOAD(CapInh)
    P_LOAD(CapPrm)
    P_LOAD(CapEff)
    P_LOAD(CapBnd)
    P_LOAD(Seccomp)
    P_LOAD(Cpus_allowed)
    P_LOAD(Cpus_allowed_list)
    P_LOAD(Mems_allowed)
    P_LOAD(Mems_allowed_list)
    P_LOAD(voluntary_ctxt_switches)
    P_LOAD(nonvoluntary_ctxt_switches)

#undef P_LOAD
#endif
}

void TaskStatFileContent::loadFromText(const string &text)
{
    size_t com1 = text.find('(');
    size_t com2 = text.find(')');

    pid = trim(text.substr(0, com1));

    tcomm = trim(text.substr(com1 + 1, com2 - com1 - 1));

    istringstream ss(text.substr(com2 + 1));

    ss >> state;
    ss >> ppid;
    ss >> pgrp;
    ss >> sid;
    ss >> tty_nr;
    ss >> tty_pgrp;
    ss >> flags;
    ss >> min_flt;
    ss >> cmin_flt;
    ss >> maj_flt;
    ss >> cmaj_flt;
    ss >> utime;
    ss >> stime;
    ss >> cutime;
    ss >> cstime;
    ss >> priority;
    ss >> nice;
    ss >> num_threads;
    ss >> it_real_value;
    ss >> start_time;
    ss >> vsize;
    ss >> rss;
    ss >> rsslim;
    ss >> start_code;
    ss >> end_code;
    ss >> start_stack;
    ss >> esp;
    ss >> eip;
    ss >> pending;
    ss >> blocked;
    ss >> sigign;
    ss >> sigcatch;
    ss >> wchan;
    ss >> place_holder_0;
    ss >> place_holder_1;
    ss >> exit_signal;
    ss >> task_cpu;
    ss >> rt_priority;
    ss >> policy;
    ss >> blkio_ticks;
    ss >> gtime;
    ss >> cgtime;
    ss >> start_data;
    ss >> end_data;
    ss >> start_brk;
    ss >> arg_start;
    ss >> arg_end;
    ss >> env_start;
    ss >> env_end;
    ss >> exit_code;

}

void TaskStatmFileContent::loadFromText(const string &text)
{
    istringstream ss(text);
    ss >> size >> resident >> shared >> trs >> lrs >> drs >> dt;
}
